(use-modules
 ;; SRFI 64 for unit testing facilities
 (srfi srfi-64)
 ;; utils - the code to be tested
 (decision-tree)
 ;; Utilities for testing
 (utils test-utils)
 ;; Dependencies for testing the code to be tested
 (dataset)
 (data-point)
 (tree))


(define TEST-DATA
  (list #(2.771244718 1.784783929 0)
        #(1.728571309 1.169761413 0)
        #(3.678319846 2.81281357 0)
        #(3.961043357 2.61995032 0)
        #(2.999208922 2.209014212 0)
        #(7.497545867 3.162953546 1)
        #(9.00220326 3.339047188 1)
        #(7.444542326 0.476683375 1)
        #(10.12493903 3.234550982 1)
        #(6.642287351 3.319983761 1)))

(define PRECISION (expt 10 -9))

(test-begin "dataset-test")

(test-group
 "dataset-empty?"

 (test-assert "dataset-empty?-1"
   (dataset-empty? empty-dataset))

 (test-assert "dataset-empty?-2"
   (not (dataset-empty? (list 1 2 3)))))

(test-group
 "dataset-first"

 (test-equal "dataset-first-1"
   #(0 0 0)
   (dataset-first (list #(0 0 0) #(1 1 1)))))

(test-group
 "dataset-rest"

 (test-equal "dataset-rest-1"
   (list #(1 1 1))
   (dataset-rest (list #(0 0 0) #(1 1 1))))

 (test-equal "dataset-rest-2"
   (list #(1 1 1)
         #(2 2 2)
         #(3 3 3))
   (dataset-rest (list #(0 0 0)
                    #(1 1 1)
                    #(2 2 2)
                    #(3 3 3)))))

(test-group
 "dataset-length"

 (test-equal "dataset-length-1"
   3
   (dataset-length (list #(0) #(1) #(2))))

 (test-equal "dataset-length-2"
   4
   (dataset-length (list #(0) #(1) #(2 3) #(3 4 5)))))


(test-group
 "dataset-filter"
 (test-equal "dataset-filter-1"
   (dataset-filter (lambda (data-point)
                  (> (data-point-get-col data-point 1) 3))
                TEST-DATA)
   (list #(7.497545867 3.162953546 1)
         #(9.00220326 3.339047188 1)
         #(10.12493903 3.234550982 1)
         #(6.642287351 3.319983761 1)))

 (test-equal "dataset-filter-2"
   (dataset-filter (lambda (data-point)
                  (< (data-point-get-col data-point 0) 2))
                TEST-DATA)
   (list #(1.728571309 1.169761413 0))))


(test-group
 "dataset-map"
 (test-assert "dataset-map-1"
   (lists-of-vectors-approximately-equal?
    (dataset-map (lambda (data-point)
                (vector-set data-point 0
                            (+ (vector-ref data-point 0) 1.0)))
              TEST-DATA)
    (list #(3.771244718 1.784783929 0)
          #(2.728571309 1.169761413 0)
          #(4.678319846 2.81281357 0)
          #(4.961043357 2.61995032 0)
          #(3.999208922 2.209014212 0)
          #(8.497545867 3.162953546 1)
          #(10.00220326 3.339047188 1)
          #(8.444542326 0.476683375 1)
          #(11.12493903 3.234550982 1)
          #(7.642287351 3.319983761 1))
    PRECISION
    (lambda (list-1-elem list-2-elem epsilon)
      (vectors-approximately-equal? list-1-elem
                                    list-2-elem
                                    epsilon
                                    approximately-equal?)))))


(test-group
 "dataset-get-col"
 (test-equal "dataset-get-col-1"
   ;; index starting at 0, it should be the following list
   (list 1 2 3)
   (dataset-get-col (list #(123 43 1)
                       #(2132435 23 2)
                       #(1244 769 3))
                 2)))


(test-group
 "data-point-get-col"

 (test-equal "data-point-get-col-1"
   32
   (data-point-get-col #(0 32 478 282 1) 1))

 (test-equal "data-point-get-col-2"
   0
   (data-point-get-col #(0 32 478 282 1) 0)))


(test-group
 "dataset-reduce"

 (test-approximate "dataset-reduce-1"
   3.0
   (dataset-reduce
    (list
     #(1 2 0.5)
     #(3 -4 1.0)
     #(5 6 1.5))
    (lambda (acc data-point)
      (+ acc (data-point-get-col data-point 2)))
    0)
   PRECISION))


(test-group
 "select-min-cost-split"
 (test-equal "select-min-cost-split should never select infinite cost"
   (make-split 0
               6.642287351
               (list
                ;; left branch data
                (list #(2.771244718 1.784783929 0)
                      #(1.728571309 1.169761413 0)
                      #(3.678319846 2.81281357 0)
                      #(3.961043357 2.61995032 0)
                      #(2.999208922 2.209014212 0))
                ;; right branch data
                (list #(7.497545867 3.162953546 1)
                      #(9.00220326 3.339047188 1)
                      #(7.444542326 0.476683375 1)
                      #(10.12493903 3.234550982 1)
                      #(6.642287351 3.319983761 1)))
               0.0)

   (select-min-cost-split
    (make-split 0
                6.642287351
                (list
                 ;; left branch data
                 (list #(2.771244718 1.784783929 0)
                       #(1.728571309 1.169761413 0)
                       #(3.678319846 2.81281357 0)
                       #(3.961043357 2.61995032 0)
                       #(2.999208922 2.209014212 0))
                 ;; right branch data
                 (list #(7.497545867 3.162953546 1)
                       #(9.00220326 3.339047188 1)
                       #(7.444542326 0.476683375 1)
                       #(10.12493903 3.234550982 1)
                       #(6.642287351 3.319983761 1)))
                0.0)
    (make-split 0 +inf.0 (list '() '()) +inf.0))))


(test-group
 "dataset-get-columns"

 (test-equal "dataset-get-columns-1"
   (list
    (list 2.771244718
          1.728571309
          3.678319846
          3.961043357
          2.999208922
          7.497545867
          9.00220326
          7.444542326
          10.12493903
          6.642287351)

    (list 1.784783929
          1.169761413
          2.81281357
          2.61995032
          2.209014212
          3.162953546
          3.339047188
          0.476683375
          3.234550982
          3.319983761)

    (list 0
          0
          0
          0
          0
          1
          1
          1
          1
          1))
   (dataset-get-columns TEST-DATA)))


(test-end "dataset-test")
